<!DOCTYPE html>


<html lang="en">
  

    <head>
      <meta charset="utf-8" />
        
      <meta
        name="viewport"
        content="width=device-width, initial-scale=1, maximum-scale=1"
      />
      <title>《JavaScript高级程序设计》- 第十一章：期约 |  欢迎</title>
  <meta name="generator" content="hexo-theme-ayer">
      
      <link rel="shortcut icon" href="/favicon.ico" />
       
<link rel="stylesheet" href="/dist/main.css">

      <link
        rel="stylesheet"
        href="https://cdn.jsdelivr.net/gh/Shen-Yu/cdn/css/remixicon.min.css"
      />
      
<link rel="stylesheet" href="/css/custom.css">
 
      <script src="https://cdn.jsdelivr.net/npm/pace-js@1.0.2/pace.min.js"></script>
       
 

      <link
        rel="stylesheet"
        href="https://cdn.jsdelivr.net/npm/@sweetalert2/theme-bulma@5.0.1/bulma.min.css"
      />
      <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11.0.19/dist/sweetalert2.min.js"></script>

      <!-- mermaid -->
      
      <style>
        .swal2-styled.swal2-confirm {
          font-size: 1.6rem;
        }
      </style>
    <link rel="alternate" href="/atom.xml" title="欢迎" type="application/atom+xml">
</head>
  </html>
</html>


<body>
  <div id="app">
    
      
    <main class="content on">
      <section class="outer">
  <article
  id="post-JavaScript学习/《JavaScript高级程序设计》- 第十一章：期约"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h1 class="article-title sea-center" style="border-left:0" itemprop="name">
  《JavaScript高级程序设计》- 第十一章：期约
</h1>
 

      
    </header>
     
    <div class="article-meta">
      <a href="/2022/02/25/JavaScript%E5%AD%A6%E4%B9%A0/%E3%80%8AJavaScript%E9%AB%98%E7%BA%A7%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1%E3%80%8B-%20%E7%AC%AC%E5%8D%81%E4%B8%80%E7%AB%A0%EF%BC%9A%E6%9C%9F%E7%BA%A6/" class="article-date">
  <time datetime="2022-02-25T13:31:46.000Z" itemprop="datePublished">2022-02-25</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/JavaScript%E5%AD%A6%E4%B9%A0/">JavaScript学习</a>
  </div>
  
<div class="word_count">
    <span class="post-time">
        <span class="post-meta-item-icon">
            <i class="ri-quill-pen-line"></i>
            <span class="post-meta-item-text"> Word count:</span>
            <span class="post-count">4.9k</span>
        </span>
    </span>

    <span class="post-time">
        &nbsp; | &nbsp;
        <span class="post-meta-item-icon">
            <i class="ri-book-open-line"></i>
            <span class="post-meta-item-text"> Reading time≈</span>
            <span class="post-count">21 min</span>
        </span>
    </span>
</div>
 
    </div>
      
    <div class="tocbot"></div>




  
    <div class="article-entry" itemprop="articleBody">
       
  <h2 id="第十一章：期约"><a href="#第十一章：期约" class="headerlink" title="第十一章：期约"></a>第十一章：期约</h2><p>介绍<code>Promise</code>之前，先介绍一下<code>异步编程</code>；</p>
<blockquote>
<p>同步行为与异步行为在计算机科学中一对对立统一的概念</p>
</blockquote>
<ol>
<li>同步行为：内存中<strong>严格按照顺序</strong>执行处理器指令。在执行的每一步，都可以推断出程序的状态。</li>
<li>异步行为：类似于<strong>系统中断</strong>，即：当前进程外部的实体可以执行的代码；</li>
</ol>
<h3 id="11-1-回调地狱"><a href="#11-1-回调地狱" class="headerlink" title="11.1 回调地狱"></a>11.1 回调地狱</h3><p>在早期<code>JavaScript</code>中，只支持定义<code>回调函数</code>来<strong>表明异步</strong>；通常需要<strong>深度嵌套的回调函数</strong>（回调地狱）来解决；</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">double</span>(<span class="params">value</span>)</span>&#123;</span><br><span class="line">    <span class="built_in">setTimeout</span>( <span class="function">() =&gt;</span> <span class="built_in">setTimeout</span>(<span class="built_in">console</span>.log, <span class="number">0</span>, value * <span class="number">2</span>), <span class="number">1000</span>);</span><br><span class="line">&#125;</span><br><span class="line">double(<span class="number">3</span>);	<span class="comment">// 大概1000毫秒后，6</span></span><br></pre></td></tr></table></figure>

<ol>
<li><p>异步操作值</p>
<blockquote>
<p>在内部<code>setTimeout</code>中可以异步操作传入值<code>value</code>；所以可以提取一个<code>回调函数</code>来利用；</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">double</span>(<span class="params">value,callback</span>)</span>&#123;</span><br><span class="line"> <span class="built_in">setTimeout</span>( <span class="function">() =&gt;</span> <span class="built_in">setTimeout</span>(<span class="function">() =&gt;</span> callback(value *<span class="number">2</span>) ), <span class="number">1000</span>);</span><br><span class="line">&#125;</span><br><span class="line">double(<span class="number">3</span>, <span class="function">(<span class="params">x</span>) =&gt;</span> &#123; <span class="built_in">console</span>.log(<span class="string">`I get the <span class="subst">$&#123;x&#125;</span>!`</span>)&#125; );	<span class="comment">// 大概1000毫秒后，I get the 6!</span></span><br></pre></td></tr></table></figure></li>
<li><p>失败处理</p>
<blockquote>
<p>上面的异步操作值，达成一种<strong>成功处理</strong>的效果。反之，也需要实现<strong>失败处理</strong>的效果；</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> successCallback  = <span class="function">(<span class="params">x</span>) =&gt;</span> &#123; <span class="built_in">console</span>.log(<span class="string">`Success：<span class="subst">$&#123;x&#125;</span>`</span>); &#125;;</span><br><span class="line"><span class="keyword">const</span> faliureCallback = <span class="function">(<span class="params">e</span>) =&gt;</span> &#123; <span class="built_in">console</span>.log(<span class="string">`Failure：<span class="subst">$&#123;e&#125;</span>`</span>); &#125;;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">double</span>(<span class="params">value,success,faliure</span>)</span>&#123;</span><br><span class="line"> <span class="built_in">setTimeout</span>( <span class="function">() =&gt;</span> &#123;</span><br><span class="line"> 	<span class="keyword">try</span>&#123;</span><br><span class="line">         <span class="keyword">if</span>( <span class="keyword">typeof</span> value !== <span class="string">&#x27;number&#x27;</span> ) &#123;</span><br><span class="line">             <span class="keyword">throw</span> <span class="string">&#x27;Yout must input a number as first argument&#x27;</span>;</span><br><span class="line">         &#125;</span><br><span class="line">         success(<span class="number">2</span>*value);	<span class="comment">// 成功处理</span></span><br><span class="line">     &#125;<span class="keyword">catch</span>(e)&#123;	</span><br><span class="line">         faliure(e);		<span class="comment">// 失败处理</span></span><br><span class="line">     &#125;</span><br><span class="line"> &#125;, <span class="number">1000</span>);</span><br><span class="line">&#125;</span><br><span class="line">double(<span class="number">3</span>, successCallback, faliureCallback);	<span class="comment">// 大概1000毫秒后，Success：6</span></span><br><span class="line">double(<span class="string">&#x27;a&#x27;</span>, successCallback, faliureCallback);	<span class="comment">// 大概1000毫秒后，Failure：Yout must input a number as first argument</span></span><br></pre></td></tr></table></figure>

<blockquote>
<p>这种模式已经不可取了，因为必须初始化异步操作时定义回调。</p>
</blockquote>
</li>
<li><p>嵌套异步回调</p>
<blockquote>
<p>假如一个异步的返回值，被另一个异步使用呢？</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">double</span>(<span class="params">value,success,faliure</span>)</span>&#123;</span><br><span class="line"> <span class="built_in">setTimeout</span>( <span class="function">() =&gt;</span> &#123;</span><br><span class="line"> 	<span class="keyword">try</span>&#123;</span><br><span class="line">         <span class="keyword">if</span>( <span class="keyword">typeof</span> value !== <span class="string">&#x27;number&#x27;</span> ) &#123;</span><br><span class="line">             <span class="keyword">throw</span> <span class="string">&#x27;Yout must input a number as first argument&#x27;</span>;</span><br><span class="line">         &#125;</span><br><span class="line">         success(<span class="number">2</span>*value);	<span class="comment">// 成功处理</span></span><br><span class="line">     &#125;<span class="keyword">catch</span>(e)&#123;	</span><br><span class="line">         faliure(e);		<span class="comment">// 失败处理</span></span><br><span class="line">     &#125;</span><br><span class="line"> &#125;, <span class="number">1000</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> successCallback  = <span class="function">(<span class="params">x</span>) =&gt;</span> &#123; double(x, <span class="function">(<span class="params">y</span>) =&gt;</span> <span class="built_in">console</span>.log(<span class="string">`Success：<span class="subst">$&#123; y &#125;</span>`</span>)) &#125;;	<span class="comment">// 嵌套调用</span></span><br><span class="line"><span class="keyword">const</span> faliureCallback = <span class="function">(<span class="params">e</span>) =&gt;</span> &#123; <span class="built_in">console</span>.log(<span class="string">`Failure：<span class="subst">$&#123;e&#125;</span>`</span>); &#125;;</span><br><span class="line"></span><br><span class="line">double(<span class="number">3</span>, successCallback, faliureCallback);	<span class="comment">// 大概1000毫秒后，Success：12</span></span><br></pre></td></tr></table></figure>



<blockquote>
<p>显然，这一层嵌套已经不好维护了。该回调策略显然不可用！（回调地狱，真的是地狱）</p>
</blockquote>
</li>
</ol>
<h3 id="11-2-期约"><a href="#11-2-期约" class="headerlink" title="11.2 期约"></a>11.2 期约</h3><p>ES6新增了Promise类型，成为了主导性的异步编程机制。如<code>fetch()</code>和<code>Battery Status API</code>等浏览器API都是以期约为基础；</p>
<h4 id="11-2-1-期约基础"><a href="#11-2-1-期约基础" class="headerlink" title="11.2.1 期约基础"></a>11.2.1 期约基础</h4><ol>
<li><p>期约需要<strong>new操作符</strong>来实例化；</p>
</li>
<li><p>创建期约的时候，需要传入执行器（不然会报错）；</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> p = <span class="keyword">new</span> <span class="built_in">Promise</span>( <span class="function">()=&gt;</span>&#123;&#125; );</span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="built_in">console</span>.log, <span class="number">0</span>, p);	<span class="comment">// Promise &#123; &lt;pending&gt; &#125;</span></span><br></pre></td></tr></table></figure></li>
<li><p>期约状态机</p>
<blockquote>
<p>期约拥有状态，且是<strong>私有的</strong>，不能直接通过<code>JavaScript</code>检测、修改；<strong>只能改变一次</strong>！</p>
</blockquote>
<ul>
<li><code>pending</code>：期约的最初始状态——<strong>待定</strong>；表示尚未开始或正在执行中。</li>
<li><code>resolved</code>：期约可以落定，落定成功即为<strong>解决</strong>（<code>resolved</code>），有时候也称<strong>兑换</strong>（<code>fulfilled</code>）；</li>
<li><code>rejected</code>：期约可以落定，落定失败即为<strong>拒绝</strong>（<code>rejected</code>）；</li>
</ul>
</li>
<li><p>解决值与拒绝理由</p>
<blockquote>
<p>当状态为兑现的时候，会返回一个<strong>解决值</strong>；当为拒绝的时候，会返回一个<strong>拒绝理由</strong>；</p>
</blockquote>
</li>
<li><p>期约的使用例子</p>
<blockquote>
<p>期约向服务器发送一个HTTP请求并预定会返回一个JSON。 如果请求返回范围在200到299的状态码， 则足以让期约的状态变为兑现。 此时期约内部就可以收到一个JSON字符串。 类似地， 如果请求返回的状态码不在200到299这个范围内， 那么就会把期约状态切换为拒绝。 此时拒绝的理由可能是一个Error对象， 包含着HTTP状态码及相关错误消息。  </p>
</blockquote>
</li>
<li><p>通过执行函数控制期约状态</p>
<blockquote>
<p>执行函数主要有两个职责：</p>
<ul>
<li>初始化期约的异步行为</li>
<li>控制状态的最终转换：<code>resolved()</code>和<code>reject()</code>;</li>
</ul>
<p>因为执行器是期约的初始化程序，所以执行器函数是<strong>同步函数</strong>；</p>
<blockquote>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> p1 = <span class="keyword">new</span> <span class="built_in">Promise</span>( <span class="function">(<span class="params">resolve,reject</span>)=&gt;</span>&#123; resolve(); &#125; );</span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="built_in">console</span>.log, <span class="number">0</span>, p1); <span class="comment">// Promise &#123; undefined &#125;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> p2 = <span class="keyword">new</span> <span class="built_in">Promise</span>( <span class="function">(<span class="params">resolve,reject</span>)=&gt;</span>&#123; reject(); &#125; );</span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="built_in">console</span>.log, <span class="number">0</span>, p2); <span class="comment">// Promise &#123; &lt;rejected&gt; undefined &#125;</span></span><br></pre></td></tr></table></figure>

<blockquote>
<p>除此之外，还可以延迟状态切换：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> p1 = <span class="keyword">new</span> <span class="built_in">Promise</span>( <span class="function">(<span class="params">resolve,reject</span>)=&gt;</span>&#123; </span><br><span class="line">	<span class="built_in">setTimeout</span>(<span class="function">()=&gt;</span>&#123; resolve() &#125;,<span class="number">5000</span>);</span><br><span class="line">&#125; );</span><br></pre></td></tr></table></figure>
</blockquote>
<blockquote>
<p>注意：状态切换是<strong>不可逆的</strong>，当进行二次切换的时候，是<strong>静默失败的</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> p1 = <span class="keyword">new</span> <span class="built_in">Promise</span>( <span class="function">(<span class="params">resolve,reject</span>)=&gt;</span>&#123; </span><br><span class="line">	resolve();</span><br><span class="line">    reject();	<span class="comment">// 静默失败</span></span><br><span class="line">&#125; );</span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="built_in">console</span>.log, <span class="number">0</span>, p1); <span class="comment">// Promise &#123; undefined &#125;</span></span><br></pre></td></tr></table></figure></blockquote>
</blockquote>
</blockquote>
</li>
<li><p><code>Promise.resolve()</code></p>
<p>期约并非一开始就必须处于<strong>创建状态</strong>；而是可以通过静态方法：<code>Promise.resolve()</code>来创建一个<strong>已解决的状态的期约</strong>；</p>
<blockquote>
<p>静态方法创建期约：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">&gt;<span class="keyword">let</span> p = <span class="built_in">Promise</span>.resolve();</span><br><span class="line">&gt;<span class="built_in">console</span>.log(p);	<span class="comment">// Promise &#123; undefined &#125;</span></span><br></pre></td></tr></table></figure>



<p>将任何值转化为期约：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&gt;<span class="keyword">let</span> p1 = <span class="built_in">Promise</span>.resolve(<span class="number">1</span>);</span><br><span class="line">&gt;<span class="keyword">let</span> p2 = <span class="built_in">Promise</span>.resolve(<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>);	<span class="comment">// 多余的参数会忽略</span></span><br><span class="line">&gt;&gt; <span class="built_in">console</span>.log(p1);    <span class="comment">// Promise &#123; 1 &#125;</span></span><br><span class="line">&gt;<span class="built_in">console</span>.log(p2);    <span class="comment">// Promise &#123; 1 &#125;</span></span><br><span class="line">&gt;<span class="built_in">console</span>.log(p1 === p2);  <span class="comment">// false;</span></span><br></pre></td></tr></table></figure>



<p>如果传入参数也是期约，则操作是幂等的</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&gt;<span class="keyword">let</span> p1 = <span class="keyword">new</span> <span class="built_in">Promise</span>( <span class="function">() =&gt;</span> &#123;&#125; );</span><br><span class="line">&gt;<span class="keyword">let</span> p2 = <span class="built_in">Promise</span>.resolve(p1);</span><br><span class="line">&gt;<span class="built_in">console</span>.log(p1 === p2);		<span class="comment">// true</span></span><br></pre></td></tr></table></figure>

</blockquote>
</li>
<li><p><code>Promise.reject()</code></p>
<p>与<code>Promise.reject()</code>类似，<code>Promise.reject()</code>会实例化一个<strong>拒绝的期约</strong>并<strong>抛出一个异步错误</strong>；这个异步错误不能被<code>try&#123;&#125;catch&#123;&#125;</code>捕获；</p>
<blockquote>
<p>其接收的<strong>第一个参数</strong>就是拒绝期约要返回的<strong>拒绝理由</strong>：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&gt;<span class="keyword">let</span> p = <span class="built_in">Promise</span>.reject(<span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">&#x27;This is a failure promise!&#x27;</span>));</span><br><span class="line"></span><br><span class="line">&gt;<span class="built_in">console</span>.log(p); <span class="comment">// Promise &#123; &lt;rejected&gt; Error: This is a failure promise! &#125;</span></span><br></pre></td></tr></table></figure></blockquote>
</li>
<li><p>同步、异步执行的二元性</p>
<p><code>Promise</code>很大程度上会导致一种完全不同的JavaScript计算模式：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span>&#123;</span><br><span class="line">    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">&#x27;This is a sync error!&#x27;</span>);</span><br><span class="line">&#125;<span class="keyword">catch</span>(e)&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(e);		<span class="comment">// 可以正常捕获错误</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">try</span>&#123;</span><br><span class="line">    <span class="built_in">Promise</span>.reject( <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">&#x27;This is a async error!&#x27;</span>) );</span><br><span class="line">&#125;<span class="keyword">catch</span>(e)&#123;</span><br><span class="line">	<span class="built_in">console</span>.log(e);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<blockquote>
<p>第二个异常，因为是期约的拒绝理由，所以不能被<code>try&#123;&#125;catch&#123;&#125;</code>捕获；</p>
<p>由此可分析出：期约抛出错误，只能通过<strong>异步模式</strong>来捕获；</p>
<p>由此可总结出：代码一旦以<strong>异步模式</strong>执行，则只能使用<strong>期约方法</strong>与其<strong>交互</strong>；</p>
<p>综上：期约就是<strong>异步模式</strong>和同步模式之间的<strong>媒介</strong></p>
</blockquote>
</li>
</ol>
<h4 id="11-2-2-期约的实例方法"><a href="#11-2-2-期约的实例方法" class="headerlink" title="11.2.2 期约的实例方法"></a>11.2.2 期约的实例方法</h4><p>期约实例是<strong>链接</strong>外部<strong>同步代码</strong>与内部异步代码之间的<strong>桥梁</strong>；而实例的方法有如下功能：</p>
<ul>
<li>访问异步操作返回值（resolve,reject）</li>
<li>处理期约成功和失败的结果(then)</li>
<li>连续对期约求值()</li>
<li>添加期约终止时才会执行的代码(finally)</li>
</ul>
<ol>
<li><p><code>Promise.prototype.then()</code></p>
<blockquote>
<p>为期约实例<strong>添加处理程序</strong>的主要方法：</p>
<ul>
<li>添加<code>onResolve</code>处理程序</li>
<li>添加<code>onReject</code>处理程序</li>
</ul>
<blockquote>
<p>以上两个参数都是<strong>可选</strong>的；但<strong>传入任何非函数类型的参数</strong>都会被<strong>静默忽略</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> p1 = <span class="keyword">new</span> <span class="built_in">Promise</span>( <span class="function">()=&gt;</span>&#123;&#125; );</span><br><span class="line">p1.then(<span class="string">&#x27;haah&#x27;</span>);		<span class="comment">// 静默忽略,不推荐这样写</span></span><br></pre></td></tr></table></figure>
</blockquote>
<blockquote>
<p>如果两者只传递一个，另一个可以使用<code>null</code>来替代，<strong>避免占用内存</strong>，也可以<strong>提高代码可读性</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">onReject</span>(<span class="params">aimPromise</span>)</span>&#123;</span><br><span class="line">    <span class="built_in">setTimeout</span>(<span class="built_in">console</span>.log,<span class="number">0</span>,aimPromise,<span class="string">&#x27;had been rejected!&#x27;</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">let</span> p1 = <span class="keyword">new</span> <span class="built_in">Promise</span>( <span class="function">(<span class="params">resolve,reject</span>)=&gt;</span>&#123;</span><br><span class="line">    <span class="built_in">setTimeout</span>(reject,<span class="number">3000</span>);</span><br><span class="line">&#125; );</span><br><span class="line">p1.then(<span class="literal">null</span>,<span class="function">()=&gt;</span>onReject(p1));	<span class="comment">// 大概三秒后:Promise &#123; &lt;pending&gt; &#125; had been rejected!</span></span><br></pre></td></tr></table></figure>
</blockquote>
<blockquote>
<p>与此同时，该方法也会<strong>返回一个新的期约实例</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">onReject</span>(<span class="params">aimPromise</span>)</span>&#123;</span><br><span class="line"> <span class="built_in">setTimeout</span>(<span class="built_in">console</span>.log,<span class="number">0</span>,aimPromise,<span class="string">&#x27;had been rejected!&#x27;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> p1 = <span class="keyword">new</span> <span class="built_in">Promise</span>( <span class="function">(<span class="params">resolve,reject</span>)=&gt;</span>&#123;</span><br><span class="line"> <span class="built_in">setTimeout</span>(reject,<span class="number">3000</span>);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> p2 = p1.then(<span class="literal">null</span>,<span class="function">()=&gt;</span>onReject(p1));</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(p1 === p2);		<span class="comment">// false;</span></span><br><span class="line"><span class="built_in">console</span>.log(p2);	<span class="comment">// 直接输出:Promise &#123; &lt;pending&gt; &#125;</span></span><br><span class="line"><span class="comment">// 大概三秒后:Promise &#123; &lt;pending&gt; &#125; had been rejected!</span></span><br></pre></td></tr></table></figure>
</blockquote>
<blockquote>
<p>关于空处理程序</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> p1 = <span class="built_in">Promise</span>.resolve(<span class="string">&#x27;title&#x27;</span>);</span><br><span class="line"><span class="keyword">let</span> p2 = p1.then();</span><br><span class="line"></span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="built_in">console</span>.log,<span class="number">0</span>,p1);	<span class="comment">// 再输出：Promise &#123; &#x27;title&#x27; &#125;</span></span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="built_in">console</span>.log,<span class="number">0</span>,p2);	<span class="comment">// 最后输出：Promise &#123; &#x27;title&#x27; &#125;</span></span><br><span class="line"><span class="built_in">console</span>.log(p1 === p2);			<span class="comment">// 先输出：false</span></span><br></pre></td></tr></table></figure>

<blockquote>
<p>显然，会返回一个<strong>状态，内容一致</strong>的<strong>新期约</strong></p>
</blockquote>
</blockquote>
<blockquote>
<p>关于<code>onResolve</code>处理程序</p>
<ul>
<li><p>该处理函数会通过<code>Promise.resolve()</code>来<strong>包装</strong>返回的<strong>新的期约实例</strong></p>
</li>
<li><p>如果<strong>没有显式</strong>的返回语句，则包装<code>undefined</code></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> p1 = <span class="built_in">Promise</span>.resolve(<span class="string">&#x27;title&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> p2 = p1.then( <span class="function">() =&gt;</span> <span class="literal">undefined</span> );</span><br><span class="line"><span class="keyword">let</span> p3 = p1.then( <span class="function">() =&gt;</span> &#123;&#125; );</span><br><span class="line"><span class="keyword">let</span> p4 = p1.then( <span class="function">() =&gt;</span> <span class="built_in">Promise</span>.resolve() );</span><br><span class="line"></span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="built_in">console</span>.log,<span class="number">0</span>,p2);   <span class="comment">// Promise &#123; undefined &#125;</span></span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="built_in">console</span>.log,<span class="number">0</span>,p3);   <span class="comment">// Promise &#123; undefined &#125;</span></span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="built_in">console</span>.log,<span class="number">0</span>,p4);   <span class="comment">// Promise &#123; undefined &#125;</span></span><br></pre></td></tr></table></figure></li>
<li><p>如果<strong>有显式</strong>返回语句，则包装该语句</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> p1 = <span class="built_in">Promise</span>.resolve(<span class="string">&#x27;title&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> p2 = p1.then( <span class="function">() =&gt;</span> <span class="string">&#x27;passage&#x27;</span> );</span><br><span class="line"><span class="keyword">let</span> p3 = p1.then( <span class="function">() =&gt;</span> <span class="built_in">Promise</span>.resolve(<span class="string">&#x27;passage&#x27;</span>) );</span><br><span class="line"></span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="built_in">console</span>.log,<span class="number">0</span>,p2);   <span class="comment">// Promise &#123; &#x27;passage&#x27; &#125;</span></span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="built_in">console</span>.log,<span class="number">0</span>,p3);   <span class="comment">// Promise &#123; &#x27;passage&#x27; &#125;</span></span><br></pre></td></tr></table></figure></li>
<li><p><strong>抛出异常</strong>，则会返回<strong>拒绝期约</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> p1 = <span class="built_in">Promise</span>.resolve(<span class="string">&#x27;title&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> p2 = p1.then( <span class="function">() =&gt;</span> &#123; <span class="keyword">throw</span> <span class="string">&#x27;this is a error&#x27;</span> &#125; );</span><br><span class="line"></span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="built_in">console</span>.log,<span class="number">0</span>,p2);   </span><br><span class="line"><span class="comment">// Promise &#123; &lt;rejected&gt; &#x27;this is a error&#x27; &#125;</span></span><br></pre></td></tr></table></figure></li>
<li><p>返回错误值，只会使用<code>Promise.resolve()</code>进行包装，而<strong>不会返回拒绝期约</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> p1 = <span class="built_in">Promise</span>.resolve(<span class="string">&#x27;title&#x27;</span>);</span><br><span class="line">                                        </span><br><span class="line"><span class="keyword">let</span> p2 = p1.then( <span class="function">() =&gt;</span> <span class="built_in">Error</span>(<span class="string">&#x27;this is a error&#x27;</span>) );</span><br><span class="line">                                        </span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="built_in">console</span>.log,<span class="number">0</span>,p2);   </span><br><span class="line"><span class="comment">// Promise &#123; Error: this is a error &#125;</span></span><br></pre></td></tr></table></figure></li>
</ul>
</blockquote>
<blockquote>
<p>关于<code>onReject</code>处理程序</p>
<ul>
<li><p>与<code>onResolve</code>处理程序类似，其也是使用<code>Promise.resolve()</code>进行包装</p>
<blockquote>
<p>因为<code>onReject</code>处理程序就是捕获异步异常行为，当捕获后就会进行处理，并返回一个已解决（<code>resolve</code>）的新期约；</p>
</blockquote>
</li>
</ul>
</blockquote>
</blockquote>
</li>
<li><p><code>Promise.prototype.catch()</code></p>
<blockquote>
<p>为期约添加<strong>拒绝处理程序</strong>，只接受一个参数：</p>
<ul>
<li><p><code>onReject</code>处理程序</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">onReject</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">setTimeout</span>(<span class="built_in">console</span>.log,<span class="number">0</span>,<span class="string">&#x27;This promise had been rejected&#x27;</span>);</span><br><span class="line">&#125;</span><br><span class="line">                                        </span><br><span class="line"><span class="keyword">let</span> p = <span class="keyword">new</span> <span class="built_in">Promise</span>( <span class="function">(<span class="params">resolve,reject</span>)=&gt;</span><span class="built_in">setTimeout</span>(reject,<span class="number">2000</span>));</span><br><span class="line">                                        </span><br><span class="line">p.catch(onReject);</span><br><span class="line"><span class="comment">//大概两秒后：This promise had been rejected</span></span><br></pre></td></tr></table></figure></li>
</ul>
<p>其实就是<code>Promise.prototype.then(null,()=&gt;onReject)</code>的<strong>语法糖</strong></p>
</blockquote>
</li>
<li><p><code>Promise.prototype.finally()</code></p>
<blockquote>
<p>为期约添加<code>onFinally</code>处理程序，这个程序在期约转化为解决或拒绝状态时，都会执行；</p>
<ul>
<li>意在避免<code>onResolve</code>或<code>onReject</code>中存在冗余的代码；</li>
<li>在<code>onFinally</code>处理程序中，<strong>无法知道期约的状态</strong>是解决还是拒绝;</li>
<li>该方法主要用于：<strong>添加清理代码</strong>。</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">&gt;<span class="function"><span class="keyword">function</span> <span class="title">onFinally</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">&gt;<span class="built_in">setTimeout</span>(<span class="built_in">console</span>.log,<span class="number">0</span>,<span class="string">&#x27;The promise is Final&#x27;</span>);</span><br><span class="line">&gt;&#125;</span><br><span class="line"></span><br><span class="line">&gt;<span class="keyword">let</span> p = <span class="keyword">new</span> <span class="built_in">Promise</span>( <span class="function">(<span class="params">resolve,reject</span>)=&gt;</span><span class="built_in">setTimeout</span>(reject,<span class="number">2000</span>));</span><br><span class="line"></span><br><span class="line">&gt;p.finally(onFinally);</span><br><span class="line">&gt;<span class="comment">//大概两秒后：The promise is Final</span></span><br></pre></td></tr></table></figure>

<p>因为<code>onFinally</code>被设计为一个与状态无关的方法；</p>
<p>所以其返回值大多数情况下表现为父期约的传递</p>
<blockquote>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> p1 = <span class="built_in">Promise</span>.resolve(<span class="string">&#x27;title&#x27;</span>);</span><br><span class="line"><span class="keyword">let</span> p2 = p1.finally( <span class="function">() =&gt;</span> <span class="literal">undefined</span> );</span><br><span class="line"><span class="keyword">let</span> p3 = p1.finally( <span class="function">() =&gt;</span> &#123;&#125; );</span><br><span class="line"><span class="keyword">let</span> p4 = p1.finally( <span class="function">() =&gt;</span> <span class="built_in">Promise</span>.resolve() );</span><br><span class="line"><span class="keyword">let</span> p5 = p1.finally( <span class="function">() =&gt;</span> <span class="string">&#x27;passage&#x27;</span> );</span><br><span class="line"><span class="keyword">let</span> p6 = p1.finally( <span class="function">() =&gt;</span> <span class="built_in">Promise</span>.resolve(<span class="string">&#x27;passage&#x27;</span>) );</span><br><span class="line"><span class="keyword">let</span> p7 = p1.finally( <span class="function">() =&gt;</span> <span class="built_in">Error</span>(<span class="string">&#x27;this is a error&#x27;</span>) );</span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="built_in">console</span>.log,<span class="number">0</span>,p2);   <span class="comment">// Promise &#123; &#x27;title&#x27; &#125;</span></span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="built_in">console</span>.log,<span class="number">0</span>,p3);   <span class="comment">// Promise &#123; &#x27;title&#x27; &#125;</span></span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="built_in">console</span>.log,<span class="number">0</span>,p4);   <span class="comment">// Promise &#123; &#x27;title&#x27; &#125;</span></span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="built_in">console</span>.log,<span class="number">0</span>,p5);   <span class="comment">// Promise &#123; &#x27;title&#x27; &#125;</span></span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="built_in">console</span>.log,<span class="number">0</span>,p6);   <span class="comment">// Promise &#123; &#x27;title&#x27; &#125;</span></span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="built_in">console</span>.log,<span class="number">0</span>,p7);   <span class="comment">// Promise &#123; &#x27;title&#x27; &#125;</span></span><br></pre></td></tr></table></figure>
</blockquote>
<p>如果<strong>返回值</strong>如下：</p>
<ul>
<li>待定期约：（少见）</li>
<li>抛出错误</li>
<li>显式返回一个拒绝期约</li>
</ul>
<p>都会<strong>返回对应的期约</strong>（待定/拒绝）</p>
<blockquote>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> p1 = <span class="built_in">Promise</span>.resolve(<span class="string">&#x27;title&#x27;</span>);</span><br><span class="line"><span class="keyword">let</span> p2 = p1.finally( <span class="function">() =&gt;</span> <span class="built_in">Promise</span>.reject(<span class="string">&#x27;passage&#x27;</span>) );</span><br><span class="line"><span class="keyword">let</span> p3 = p1.finally( <span class="function">() =&gt;</span> &#123; <span class="keyword">throw</span> <span class="string">&#x27;error&#x27;</span>&#125; );</span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="built_in">console</span>.log,<span class="number">0</span>,p2);   <span class="comment">// Promise &#123; &lt;rejected&gt; &#x27;passage&#x27; &#125;</span></span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="built_in">console</span>.log,<span class="number">0</span>,p3);   <span class="comment">// Promise &#123; &lt;rejected&gt; &#x27;error&#x27; &#125;</span></span><br></pre></td></tr></table></figure>
</blockquote>
</blockquote>
</li>
<li><p>非重入期约方法</p>
<ul>
<li>当期约进入落定状态时，与该状态相关的处理程序仅仅会被<strong>排期</strong>，而<strong>不是立即执行</strong>；</li>
<li>跟在这些处理程序后面的同步代码，一定会比处理程序先执行</li>
<li>以上称为“非重入”特性</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> p = <span class="built_in">Promise</span>.resolve();  <span class="comment">// 创建已解决期约</span></span><br><span class="line"></span><br><span class="line">p.then(<span class="function">()=&gt;</span>&#123;</span><br><span class="line">    <span class="built_in">setTimeout</span>(<span class="built_in">console</span>.log,<span class="number">0</span>,<span class="string">&#x27;resolved&#x27;</span>);</span><br><span class="line">&#125;,<span class="literal">null</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">&#x27;this code is after p.then()!&#x27;</span>);</span><br><span class="line"><span class="comment">// 实际上输出：</span></span><br><span class="line"><span class="comment">// this code is after p.then()!</span></span><br><span class="line"><span class="comment">// resolved</span></span><br></pre></td></tr></table></figure>

<p>当<code>p.then()</code>调用<code>onResolve</code>处理程序的时候，<strong>会将其推进消息队列</strong>；当线程上<strong>同步代码处理完毕后</strong>，<strong>才会处理消息队列中的异步代码</strong>；</p>
<blockquote>
<p><strong>非重入</strong>非常适合<code>onResolve</code>/<code>onReject</code>/<code>catch</code>/<code>finally</code>处理程序；</p>
</blockquote>
</li>
<li><p><strong>临近处理程序</strong>的执行顺序</p>
<blockquote>
<p>显然，非重入说明了<strong>同步代码</strong>与<strong>异步代码</strong>之间的<strong>执行顺序</strong>；</p>
<p>然而，对于<strong>处理程序</strong>即异步代码，则按照其添加顺序执行。</p>
</blockquote>
</li>
<li><p>传递<strong>解决值</strong>与<strong>拒绝理由</strong></p>
<p>当状态落定后，期约会提供解决值或拒绝理由，并作为<code>resolve()</code>或<code>reject()</code>的第一个参数向后传递；</p>
</li>
<li><p><strong>拒绝期约</strong>与<strong>拒绝错误</strong>处理</p>
<p>拒绝期约代表了一种程序状态，即<strong>需要中断或特殊处理</strong>；在期约<strong>执行器函数</strong>或处理程序中<strong>抛出错误</strong>，则会导致<strong>拒绝</strong>，对于的<strong>错误对象</strong>，就是<strong>拒绝理由</strong>。</p>
<blockquote>
<p>拒绝期约也是一种中断或需要特殊处理的信号，为什么就不会中断程序呢？</p>
<p>答：因为期约的错误，实际上是在<strong>消息队列</strong>中抛出的，所以不会阻止运行中的程序。</p>
</blockquote>
</li>
</ol>
<h4 id="11-2-3-期约连锁与期约合成"><a href="#11-2-3-期约连锁与期约合成" class="headerlink" title="11.2.3 期约连锁与期约合成"></a>11.2.3 期约连锁与期约合成</h4><ul>
<li>期约连锁：将期约一个一个拼接起来</li>
<li>期约合成：将多个期约合并为一个期约</li>
</ul>
<ol>
<li><p>期约连锁：</p>
<p>由于期约的实例方法，都会<strong>返回一个新的期约实例</strong>，所以可以形成<strong>链式连锁调用</strong>；</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> p = <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve,reject</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">setTimeout</span>(<span class="built_in">console</span>.log,<span class="number">0</span>,<span class="string">&#x27;first&#x27;</span>);</span><br><span class="line">    resolve();</span><br><span class="line">&#125;);</span><br><span class="line">p.then(<span class="function">() =&gt;</span> &#123; <span class="built_in">setTimeout</span>(<span class="built_in">console</span>.log,<span class="number">0</span>,<span class="string">&#x27;second&#x27;</span>) &#125;,<span class="literal">null</span>)</span><br><span class="line"> .then(<span class="function">() =&gt;</span> &#123; <span class="built_in">setTimeout</span>(<span class="built_in">console</span>.log,<span class="number">0</span>,<span class="string">&#x27;third&#x27;</span>) &#125;,<span class="literal">null</span>)</span><br><span class="line"> .then(<span class="function">() =&gt;</span> &#123; <span class="built_in">setTimeout</span>(<span class="built_in">console</span>.log,<span class="number">0</span>,<span class="string">&#x27;fourth&#x27;</span>) &#125;,<span class="literal">null</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// first</span></span><br><span class="line"><span class="comment">// second</span></span><br><span class="line"><span class="comment">// third</span></span><br><span class="line"><span class="comment">// fourth</span></span><br></pre></td></tr></table></figure>

<p>显然，这样链式调用也可以使用在其他实例方法上</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> p = <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve,reject</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">setTimeout</span>(<span class="built_in">console</span>.log,<span class="number">0</span>,<span class="string">&#x27;new&#x27;</span>);</span><br><span class="line">    reject();</span><br><span class="line">&#125;)</span><br><span class="line">p.then( <span class="function">()=&gt;</span> <span class="built_in">setTimeout</span>(<span class="built_in">console</span>.log,<span class="number">0</span>,<span class="string">&#x27;then&#x27;</span>) )</span><br><span class="line"> .catch( <span class="function">()=&gt;</span> <span class="built_in">setTimeout</span>(<span class="built_in">console</span>.log,<span class="number">0</span>,<span class="string">&#x27;catch&#x27;</span>) )</span><br><span class="line"> .finally( <span class="function">()=&gt;</span> <span class="built_in">setTimeout</span>(<span class="built_in">console</span>.log,<span class="number">0</span>,<span class="string">&#x27;finally&#x27;</span>) )</span><br><span class="line"><span class="comment">// new</span></span><br><span class="line"><span class="comment">// catch</span></span><br><span class="line"><span class="comment">// finally</span></span><br></pre></td></tr></table></figure></li>
<li><p>期约图：</p>
<p>由上可知，期约可以由多个处理程序，所以就会形成一个有向非循环的图；</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> A = <span class="keyword">new</span> <span class="built_in">Promise</span>( <span class="function">(<span class="params">resolve,reject</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">&#x27;A&#x27;</span>);</span><br><span class="line">    resolve();</span><br><span class="line">&#125; );</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> B = A.then( <span class="function">()=&gt;</span> &#123; <span class="built_in">console</span>.log(<span class="string">&quot;B&quot;</span>); &#125; );</span><br><span class="line"><span class="keyword">let</span> C = A.then( <span class="function">()=&gt;</span> &#123; <span class="built_in">console</span>.log(<span class="string">&quot;C&quot;</span>); &#125; );</span><br><span class="line"></span><br><span class="line">C.then( <span class="function">() =&gt;</span> &#123; <span class="built_in">console</span>.log(<span class="string">&quot;D&quot;</span>) &#125; );</span><br><span class="line">C.then( <span class="function">() =&gt;</span> &#123; <span class="built_in">console</span>.log(<span class="string">&quot;E&quot;</span>) &#125; );</span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">    A</span></span><br><span class="line"><span class="comment">   / \</span></span><br><span class="line"><span class="comment">  B   C</span></span><br><span class="line"><span class="comment">     / \</span></span><br><span class="line"><span class="comment">    D   E</span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure></li>
<li><p>期约合成：</p>
<blockquote>
<p>Promise类提供了两种期约合成的静态方法；</p>
<ul>
<li><code>Promise.all()</code></li>
<li><code>Promise.race()</code></li>
</ul>
</blockquote>
<blockquote>
<p>关于<code>Promise.all()</code>：</p>
<ul>
<li>接收一个可迭代的对象，即一组期约；</li>
<li>一组期约全部解决后，返回一个已解决的新期约</li>
<li>若其中一个待定状态，则用于待定</li>
<li>若其中一个拒绝状态，则返回第一个拒绝期约的拒绝理由；</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> all = <span class="built_in">Promise</span>.all([</span><br><span class="line">    <span class="built_in">Promise</span>.resolve(<span class="string">&#x27;1&#x27;</span>),</span><br><span class="line">    <span class="keyword">new</span> <span class="built_in">Promise</span>( <span class="function">(<span class="params">resolve,reject</span>) =&gt;</span> &#123; <span class="built_in">setTimeout</span>(resolve,<span class="number">2000</span>) &#125; )</span><br><span class="line">])</span><br><span class="line"></span><br><span class="line">all.then( <span class="function">()=&gt;</span> &#123; <span class="built_in">setTimeout</span>(<span class="built_in">console</span>.log,<span class="number">0</span>,<span class="string">&#x27;all resolved!&#x27;</span>)&#125; );</span><br><span class="line"><span class="comment">// 大概两秒后， all resolved！</span></span><br></pre></td></tr></table></figure>
</blockquote>
<blockquote>
<p>关于<code>Promise.race()</code></p>
<ul>
<li>接收一个可迭代的对象，即一组期约；</li>
<li>返回一组期约中，<strong>最先</strong>解决或拒绝的期约的镜像</li>
<li>不管是解决还是拒绝，都会包装其返回值或拒绝理由后，返回一个新的期约；</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> first = <span class="built_in">Promise</span>.all([</span><br><span class="line">    <span class="keyword">new</span> <span class="built_in">Promise</span>( <span class="function">()=&gt;</span>&#123;&#125; ),	<span class="comment">//待定的期约</span></span><br><span class="line">    <span class="built_in">Promise</span>.reject(<span class="string">&#x27;error&#x27;</span>),</span><br><span class="line">    <span class="built_in">Promise</span>.resolve(<span class="string">&#x27;success&#x27;</span>)</span><br><span class="line">])</span><br><span class="line"></span><br><span class="line">first.catch( <span class="function">(<span class="params">reason</span>) =&gt;</span> &#123; <span class="built_in">setTimeout</span>(<span class="built_in">console</span>.log,<span class="number">0</span>,reason)&#125; );</span><br></pre></td></tr></table></figure></blockquote>
</li>
</ol>
<h3 id="11-3-异步函数"><a href="#11-3-异步函数" class="headerlink" title="11.3 异步函数"></a>11.3 异步函数</h3><p>异步函数，又被称为“<code>async</code>/<code>await</code>”（语法关键字）；是ES8规范新增的；</p>
<p>其<strong>作用</strong>在于：<strong>以同步方式写的代码可以异步执行</strong>。</p>
<h4 id="11-3-1-async"><a href="#11-3-1-async" class="headerlink" title="11.3.1 async"></a>11.3.1 <code>async</code></h4><p>可以让函数具有<strong>异步特征</strong>；</p>
<ol>
<li><p>使用<code>async</code></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">function</span> <span class="title">show</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line">show();</span><br><span class="line"><span class="built_in">console</span>.log(<span class="number">2</span>);</span><br><span class="line"><span class="comment">// 1</span></span><br><span class="line"><span class="comment">// 2</span></span><br></pre></td></tr></table></figure></li>
<li><p>异步函数返回值</p>
<p>如果异步函数使用<code>return</code>返回值，则该值会被<code>Promise.resolve()</code>包装为一个期约对象</p>
</li>
<li><p>注意：</p>
<blockquote>
<p>其实<code>async</code>声明异步函数，就是给<code>await</code>提供使用环境的，单纯使用<code>async</code>声明异步函数，<strong>与普通函数无异</strong>；</p>
</blockquote>
</li>
</ol>
<h4 id="11-3-2-await"><a href="#11-3-2-await" class="headerlink" title="11.3.2 await"></a>11.3.2 <code>await</code></h4><p>暂停异步代码的执行，等待期约解决；</p>
<ol>
<li><p>使用<code>await</code></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">function</span> <span class="title">show</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">	<span class="built_in">console</span>.log(<span class="number">1</span>);</span><br><span class="line">    <span class="keyword">let</span> p = <span class="keyword">new</span> <span class="built_in">Promise</span>( <span class="function">(<span class="params">resolve,reject</span>) =&gt;</span> &#123;</span><br><span class="line">        <span class="built_in">setTimeout</span>(resolve,<span class="number">2000</span>,<span class="number">3</span>);</span><br><span class="line">    &#125;)</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="keyword">await</span> p);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<blockquote>
<p>注意: <code>await</code>会<strong>暂停</strong>执行<strong>异步函数后面的代码</strong>,<strong>让出</strong><code>JavaScript</code>运行时的执行<strong>线程</strong> ;</p>
</blockquote>
</li>
<li><p><code>await</code>的限制</p>
<ul>
<li>必须在<code>async</code>函数中使用</li>
<li>不能在顶级上下文，如：script标签、模块中使用；</li>
</ul>
</li>
</ol>
<h4 id="11-3-3-理解异步函数执行顺序"><a href="#11-3-3-理解异步函数执行顺序" class="headerlink" title="11.3.3 理解异步函数执行顺序"></a>11.3.3 理解异步函数执行顺序</h4><p><code>await</code>关键字并非只是等待一个值可用而已；<code>JavaScript</code>运行时在碰到<code>await</code>关键字的时候，会<strong>记录在哪里暂停执行</strong>，等到<code>await</code><strong>右边的值可用的时候</strong>，JavaScript运行时会<strong>向消息队列推送一个任务</strong>，这个任务会恢复异步函数的执行。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">function</span> <span class="title">show</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="number">2</span>);</span><br><span class="line">    <span class="keyword">await</span> <span class="literal">null</span>;</span><br><span class="line">    conosole.log(<span class="number">4</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(<span class="number">1</span>);</span><br><span class="line">show();</span><br><span class="line"><span class="built_in">console</span>.log(<span class="number">3</span>)</span><br></pre></td></tr></table></figure>

<blockquote>
<p>  (1) 打印1；</p>
<p>  (2) 调用异步函数show(()；</p>
<p>  (3)（在show()中） 打印2；</p>
<p>  (4)（在show()中） await关键字暂停执行， 为立即可用的值null向消息队列中添加一个任务；</p>
<p>  (5) show()退出；</p>
<p>  (6) 打印3；</p>
<p>  (7) 同步线程的代码执行完毕；</p>
<p>  (8) JavaScript运行时从消息队列中取出任务， 恢复异步函数执行；</p>
<p>  (9)（在show()中） 恢复执行， await取得null值（这里并没有使用） ；</p>
<p>  (10)（在show()中） 打印4；</p>
<p>  (11) show()返回。</p>
</blockquote>
<p>注意：如果<code>await</code><strong>等待的不是一个立即可用的值</strong>，则会发生另外的改变；</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="number">2</span>);</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="keyword">await</span> <span class="built_in">Promise</span>.resolve(<span class="number">8</span>));</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="number">9</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">function</span> <span class="title">bar</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="number">4</span>);</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="keyword">await</span> <span class="number">6</span>);</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="number">7</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">console</span>.log(<span class="number">1</span>);</span><br><span class="line">foo();</span><br><span class="line"><span class="built_in">console</span>.log(<span class="number">3</span>);</span><br><span class="line">bar();</span><br><span class="line"><span class="built_in">console</span>.log(<span class="number">5</span>);</span><br></pre></td></tr></table></figure>

<blockquote>
<p>(1) 打印1；</p>
<p>(2) 调用异步函数foo()；</p>
<p>(3)（在foo()中） 打印2；</p>
<p>(4)（在foo()中） await关键字暂停执行， 向消息队列中添加一个期约在落定之后执行的任务；</p>
<p>(5) 期约立即落定， 把给await提供值的任务添加到消息队列；</p>
<p>(6) foo()退出；</p>
<p>(7) 打印3；</p>
<p>(8) 调用异步函数bar()；</p>
<p>(9)（ 在bar()中） 打印4；</p>
<p>(10)（ 在bar()中） await关键字暂停执行， 为立即可用的值6向消息队列中添加一个任务；</p>
<p>(11) bar()退出；</p>
<p>(12) 打印5；</p>
<p>(13) 顶级线程执行完毕；</p>
<p>(14) JavaScript运行时从消息队列中取出解决await期约的处理程序， 并将解决的值8提供给它；</p>
<p>(15) JavaScript运行时向消息队列中添加一个恢复执行foo()函数的任务；</p>
<p>(16) JavaScript运行时从消息队列中取出恢复执行bar()的任务及值6；</p>
<p>(17)（ 在bar()中） 恢复执行， await取得值6；</p>
<p>(18)（ 在bar()中） 打印6；</p>
<p>(19)（ 在bar()中） 打印7；</p>
<p>(20) bar()返回；</p>
<p>(21) 异步任务完成， JavaScript从消息队列中取出恢复执行foo()的任务及值8；</p>
<p>(22)（ 在foo()中） 打印8；</p>
<p>(23)（ 在foo()中） 打印9；</p>
<p>(24) foo()返回。  </p>
</blockquote>
<h4 id="11-3-4-异步函数策略"><a href="#11-3-4-异步函数策略" class="headerlink" title="11.3.4 异步函数策略"></a>11.3.4 异步函数策略</h4><ol>
<li><p>实现<code>sleep()</code></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">function</span> <span class="title">sleep</span>(<span class="params">delay</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>( <span class="function">(<span class="params">resolve</span>) =&gt;</span> <span class="built_in">setTimeout</span>(resolve,delay));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">function</span> <span class="title">test</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="keyword">const</span> startTime = <span class="built_in">Date</span>.now();</span><br><span class="line">    <span class="keyword">await</span> sleep(<span class="number">3000</span>);          <span class="comment">// 利用await暂停3秒左右</span></span><br><span class="line">    <span class="built_in">console</span>.log(<span class="built_in">Date</span>.now() - startTime);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">test(); </span><br></pre></td></tr></table></figure></li>
<li><p>利用平行执行</p>
</li>
<li><p>串行执行期约</p>
</li>
<li><p>栈追踪与内存管理</p>
<blockquote>
<p><strong>期约</strong>与<strong>异步函数</strong>，虽然<strong>功能上</strong>很大程度上是<strong>重叠</strong>的，但是它们在<strong>内存中</strong>的表示则<strong>差别很大</strong>；</p>
<p>因为<code>JavaScript</code>引擎在创建期约的时<strong>尽可能保留完整的调用栈</strong>；在抛出错误的时候，调用栈可以由运行时的错误处理逻辑来获取，因而就会出现在栈追踪信息中。这就意味着<strong>需要额外支付内存</strong>，从而<strong>带来计算、存储成本</strong>。</p>
</blockquote>
</li>
</ol>
<h3 id="11-4-小结"><a href="#11-4-小结" class="headerlink" title="11.4 小结"></a>11.4 小结</h3><blockquote>
<p> 关于期约</p>
<ul>
<li>主要功能是为异步代码提供了清晰的抽象；</li>
<li>是一种<strong>可塑性极强</strong>的一种结构;</li>
<li>可以被序列化、连锁使用、复合、拓展和重组。</li>
<li>在需要串行异步代码时，期约的<strong>价值最为突出</strong>。</li>
</ul>
</blockquote>
<blockquote>
<p>关于异步函数</p>
<ul>
<li>将期约应用于JavaScript函数中的结果；</li>
<li>异步函数可以暂停执行，而不阻塞主线程。</li>
</ul>
</blockquote>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <div class="declare">
      <ul class="post-copyright">
        <li>
          <i class="ri-copyright-line"></i>
          <strong>Copyright： </strong>
          
          Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
          
        </li>
      </ul>
    </div>
    
    <footer class="article-footer">
       
<div class="share-btn">
      <span class="share-sns share-outer">
        <i class="ri-share-forward-line"></i>
        分享
      </span>
      <div class="share-wrap">
        <i class="arrow"></i>
        <div class="share-icons">
          
          <a class="weibo share-sns" href="javascript:;" data-type="weibo">
            <i class="ri-weibo-fill"></i>
          </a>
          <a class="weixin share-sns wxFab" href="javascript:;" data-type="weixin">
            <i class="ri-wechat-fill"></i>
          </a>
          <a class="qq share-sns" href="javascript:;" data-type="qq">
            <i class="ri-qq-fill"></i>
          </a>
          <a class="douban share-sns" href="javascript:;" data-type="douban">
            <i class="ri-douban-line"></i>
          </a>
          <!-- <a class="qzone share-sns" href="javascript:;" data-type="qzone">
            <i class="icon icon-qzone"></i>
          </a> -->
          
          <a class="facebook share-sns" href="javascript:;" data-type="facebook">
            <i class="ri-facebook-circle-fill"></i>
          </a>
          <a class="twitter share-sns" href="javascript:;" data-type="twitter">
            <i class="ri-twitter-fill"></i>
          </a>
          <a class="google share-sns" href="javascript:;" data-type="google">
            <i class="ri-google-fill"></i>
          </a>
        </div>
      </div>
</div>

<div class="wx-share-modal">
    <a class="modal-close" href="javascript:;"><i class="ri-close-circle-line"></i></a>
    <p>扫一扫，分享到微信</p>
    <div class="wx-qrcode">
      <img src="//api.qrserver.com/v1/create-qr-code/?size=150x150&data=http://example.com/2022/02/25/JavaScript%E5%AD%A6%E4%B9%A0/%E3%80%8AJavaScript%E9%AB%98%E7%BA%A7%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1%E3%80%8B-%20%E7%AC%AC%E5%8D%81%E4%B8%80%E7%AB%A0%EF%BC%9A%E6%9C%9F%E7%BA%A6/" alt="微信分享二维码">
    </div>
</div>

<div id="share-mask"></div>  
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/JavaScript/" rel="tag">JavaScript</a></li></ul>

    </footer>
  </div>

   
  <nav class="article-nav">
    
      <a href="/2022/02/26/JavaScript%E5%AD%A6%E4%B9%A0/%E3%80%8AJavaScript%E9%AB%98%E7%BA%A7%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1%E3%80%8B-%20%E7%AC%AC%E5%8D%81%E4%BA%8C%E7%AB%A0%EF%BC%9ABOM/" class="article-nav-link">
        <strong class="article-nav-caption">上一篇</strong>
        <div class="article-nav-title">
          
            《JavaScript高级程序设计》- 第十二章：BOM
          
        </div>
      </a>
    
    
      <a href="/2022/02/23/JavaScript%E5%AD%A6%E4%B9%A0/%E3%80%8AJavaScript%E9%AB%98%E7%BA%A7%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1%E3%80%8B-%20%E7%AC%AC%E5%8D%81%E7%AB%A0%EF%BC%9A%E5%87%BD%E6%95%B0/" class="article-nav-link">
        <strong class="article-nav-caption">下一篇</strong>
        <div class="article-nav-title">《JavaScript高级程序设计》- 第十章：函数</div>
      </a>
    
  </nav>

   
<!-- valine评论 -->
<div id="vcomments-box">
  <div id="vcomments"></div>
</div>
<script src="//cdn1.lncld.net/static/js/3.0.4/av-min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/valine@1.4.14/dist/Valine.min.js"></script>
<script>
  new Valine({
    el: "#vcomments",
    app_id: "jeb2pvxjdeJQOj5OG3AUavGv-gzGzoHsz",
    app_key: "6YlB9tQJTstHMM8GOwFu4uyY",
    path: window.location.pathname,
    avatar: "monsterid",
    placeholder: "给我的文章加点评论吧~",
    recordIP: true,
  });
  const infoEle = document.querySelector("#vcomments .info");
  if (infoEle && infoEle.childNodes && infoEle.childNodes.length > 0) {
    infoEle.childNodes.forEach(function (item) {
      item.parentNode.removeChild(item);
    });
  }
</script>
<style>
  #vcomments-box {
    padding: 5px 30px;
  }

  @media screen and (max-width: 800px) {
    #vcomments-box {
      padding: 5px 0px;
    }
  }

  #vcomments-box #vcomments {
    background-color: #fff;
  }

  .v .vlist .vcard .vh {
    padding-right: 20px;
  }

  .v .vlist .vcard {
    padding-left: 10px;
  }
</style>

 
   
     
</article>

</section>
      <footer class="footer">
  <div class="outer">
    <ul>
      <li>
        Copyrights &copy;
        2022
        <i class="ri-heart-fill heart_icon"></i> 子与子乘
      </li>
    </ul>
    <ul>
      <li>
        
      </li>
    </ul>
    <ul>
      <li>
        
        
        <span>
  <span><i class="ri-user-3-fill"></i>Visitors:<span id="busuanzi_value_site_uv"></span></span>
  <span class="division">|</span>
  <span><i class="ri-eye-fill"></i>Views:<span id="busuanzi_value_page_pv"></span></span>
</span>
        
      </li>
    </ul>
    <ul>
      
    </ul>
    <ul>
      
    </ul>
    <ul>
      <li>
        <!-- cnzz统计 -->
        
      </li>
    </ul>
  </div>
</footer>    
    </main>
    <div class="float_btns">
      <div class="totop" id="totop">
  <i class="ri-arrow-up-line"></i>
</div>

<div class="todark" id="todark">
  <i class="ri-moon-line"></i>
</div>

    </div>
    <aside class="sidebar on">
      <button class="navbar-toggle"></button>
<nav class="navbar">
  
  <div class="logo">
    <a href="/"><img src="/images/ayer-side.svg" alt="欢迎"></a>
  </div>
  
  <ul class="nav nav-main">
    
    <li class="nav-item">
      <a class="nav-item-link" href="/">主页</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/archives">归档</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/categories">分类</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags">标签</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/friends">友链</a>
    </li>
    
  </ul>
</nav>
<nav class="navbar navbar-bottom">
  <ul class="nav">
    <li class="nav-item">
      
      <a class="nav-item-link nav-item-search"  title="Search">
        <i class="ri-search-line"></i>
      </a>
      
      
    </li>
  </ul>
</nav>
<div class="search-form-wrap">
  <div class="local-search local-search-plugin">
  <input type="search" id="local-search-input" class="local-search-input" placeholder="Search...">
  <div id="local-search-result" class="local-search-result"></div>
</div>
</div>
    </aside>
    <div id="mask"></div>

<!-- #reward -->
<div id="reward">
  <span class="close"><i class="ri-close-line"></i></span>
  <p class="reward-p"><i class="ri-cup-line"></i></p>
  <div class="reward-box">
    
    
  </div>
</div>
    
<script src="/js/jquery-3.6.0.min.js"></script>
 
<script src="/js/lazyload.min.js"></script>

<!-- Tocbot -->
 
<script src="/js/tocbot.min.js"></script>

<script>
  tocbot.init({
    tocSelector: ".tocbot",
    contentSelector: ".article-entry",
    headingSelector: "h1, h2, h3, h4, h5, h6",
    hasInnerContainers: true,
    scrollSmooth: true,
    scrollContainer: "main",
    positionFixedSelector: ".tocbot",
    positionFixedClass: "is-position-fixed",
    fixedSidebarOffset: "auto",
  });
</script>

<script src="https://cdn.jsdelivr.net/npm/jquery-modal@0.9.2/jquery.modal.min.js"></script>
<link
  rel="stylesheet"
  href="https://cdn.jsdelivr.net/npm/jquery-modal@0.9.2/jquery.modal.min.css"
/>
<script src="https://cdn.jsdelivr.net/npm/justifiedGallery@3.7.0/dist/js/jquery.justifiedGallery.min.js"></script>

<script src="/dist/main.js"></script>

<!-- ImageViewer -->
 <!-- Root element of PhotoSwipe. Must have class pswp. -->
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">

    <!-- Background of PhotoSwipe. 
         It's a separate element as animating opacity is faster than rgba(). -->
    <div class="pswp__bg"></div>

    <!-- Slides wrapper with overflow:hidden. -->
    <div class="pswp__scroll-wrap">

        <!-- Container that holds slides. 
            PhotoSwipe keeps only 3 of them in the DOM to save memory.
            Don't modify these 3 pswp__item elements, data is added later on. -->
        <div class="pswp__container">
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
        </div>

        <!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
        <div class="pswp__ui pswp__ui--hidden">

            <div class="pswp__top-bar">

                <!--  Controls are self-explanatory. Order can be changed. -->

                <div class="pswp__counter"></div>

                <button class="pswp__button pswp__button--close" title="Close (Esc)"></button>

                <button class="pswp__button pswp__button--share" style="display:none" title="Share"></button>

                <button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>

                <button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>

                <!-- Preloader demo http://codepen.io/dimsemenov/pen/yyBWoR -->
                <!-- element will get class pswp__preloader--active when preloader is running -->
                <div class="pswp__preloader">
                    <div class="pswp__preloader__icn">
                        <div class="pswp__preloader__cut">
                            <div class="pswp__preloader__donut"></div>
                        </div>
                    </div>
                </div>
            </div>

            <div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
                <div class="pswp__share-tooltip"></div>
            </div>

            <button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
            </button>

            <button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
            </button>

            <div class="pswp__caption">
                <div class="pswp__caption__center"></div>
            </div>

        </div>

    </div>

</div>

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/default-skin/default-skin.min.css">
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe-ui-default.min.js"></script>

<script>
    function viewer_init() {
        let pswpElement = document.querySelectorAll('.pswp')[0];
        let $imgArr = document.querySelectorAll(('.article-entry img:not(.reward-img)'))

        $imgArr.forEach(($em, i) => {
            $em.onclick = () => {
                // slider展开状态
                // todo: 这样不好，后面改成状态
                if (document.querySelector('.left-col.show')) return
                let items = []
                $imgArr.forEach(($em2, i2) => {
                    let img = $em2.getAttribute('data-idx', i2)
                    let src = $em2.getAttribute('data-target') || $em2.getAttribute('src')
                    let title = $em2.getAttribute('alt')
                    // 获得原图尺寸
                    const image = new Image()
                    image.src = src
                    items.push({
                        src: src,
                        w: image.width || $em2.width,
                        h: image.height || $em2.height,
                        title: title
                    })
                })
                var gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, {
                    index: parseInt(i)
                });
                gallery.init()
            }
        })
    }
    viewer_init()
</script> 
<!-- MathJax -->

<!-- Katex -->

<!-- busuanzi  -->
 
<script src="/js/busuanzi-2.3.pure.min.js"></script>
 
<!-- ClickLove -->

<!-- ClickBoom1 -->

<!-- ClickBoom2 -->

<!-- CodeCopy -->
 
<link rel="stylesheet" href="/css/clipboard.css">
 <script src="https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js"></script>
<script>
  function wait(callback, seconds) {
    var timelag = null;
    timelag = window.setTimeout(callback, seconds);
  }
  !function (e, t, a) {
    var initCopyCode = function(){
      var copyHtml = '';
      copyHtml += '<button class="btn-copy" data-clipboard-snippet="">';
      copyHtml += '<i class="ri-file-copy-2-line"></i><span>COPY</span>';
      copyHtml += '</button>';
      $(".highlight .code pre").before(copyHtml);
      $(".article pre code").before(copyHtml);
      var clipboard = new ClipboardJS('.btn-copy', {
        target: function(trigger) {
          return trigger.nextElementSibling;
        }
      });
      clipboard.on('success', function(e) {
        let $btn = $(e.trigger);
        $btn.addClass('copied');
        let $icon = $($btn.find('i'));
        $icon.removeClass('ri-file-copy-2-line');
        $icon.addClass('ri-checkbox-circle-line');
        let $span = $($btn.find('span'));
        $span[0].innerText = 'COPIED';
        
        wait(function () { // 等待两秒钟后恢复
          $icon.removeClass('ri-checkbox-circle-line');
          $icon.addClass('ri-file-copy-2-line');
          $span[0].innerText = 'COPY';
        }, 2000);
      });
      clipboard.on('error', function(e) {
        e.clearSelection();
        let $btn = $(e.trigger);
        $btn.addClass('copy-failed');
        let $icon = $($btn.find('i'));
        $icon.removeClass('ri-file-copy-2-line');
        $icon.addClass('ri-time-line');
        let $span = $($btn.find('span'));
        $span[0].innerText = 'COPY FAILED';
        
        wait(function () { // 等待两秒钟后恢复
          $icon.removeClass('ri-time-line');
          $icon.addClass('ri-file-copy-2-line');
          $span[0].innerText = 'COPY';
        }, 2000);
      });
    }
    initCopyCode();
  }(window, document);
</script>
 
<!-- CanvasBackground -->

<script>
  if (window.mermaid) {
    mermaid.initialize({ theme: "forest" });
  }
</script>


    
    

  </div>
</body>

</html>